import os, math, datetime, logging, random, shutil, LogInspection
import pandas as pd
import PyQt5.QtCore as pqc

syn_logger = logging.getLogger('SyntheticalTool')
syn_logger.setLevel(logging.INFO)
fmt = logging.Formatter(
    "%(asctime)s--%(name)s-%(threadName)s-%(levelname)s-%(module)s-%(funcName)s-%(message)s-%(lineno)d")
fh = logging.FileHandler(os.path.join(LogInspection.ins_log_path, "analysis.log"), encoding='utf-8')
fh.setLevel(logging.DEBUG)
fh.setFormatter(fmt)
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
ch.setFormatter(fmt)
syn_logger.addHandler(ch)
syn_logger.addHandler(fh)


class bianmu_read:
    def __init__(self, filedir):
        self.dirpath = filedir
        self.main()

    def main(self):
        syn_logger.info('bianmu result read start')
        self.batch_outdir = os.path.join('./out/', datetime.datetime.now().strftime('%Y%m%d%H%M%S'))
        os.mkdir(self.batch_outdir)
        file_list = os.listdir(self.dirpath)
        for file in file_list:
            if 'xlsx' in file or 'xls' in file:
                self.bianmu_file = file
                self.bianmu_result = pd.read_excel(os.path.join(self.dirpath, self.bianmu_file), index_col=0)
            elif 'jeew' in file or 'JEEW' in file:
                self.jeew_dir = os.path.join(self.dirpath, file)  # 日志文件夹
            else:
                self.eew_dir = os.path.join(self.dirpath, file)  # 日志文件夹
        self.excel_parse()

    def excel_parse(self):
        self.evt_num = len(self.bianmu_result)
        # make output dir path for eew and jeew
        if 'eew_dir' in vars(self):
            eew_list = os.listdir(self.eew_dir)
            self.eew_list = eew_list
            self.eew_outdir = os.path.join(self.batch_outdir, 'EEW')
            os.mkdir(self.eew_outdir)
        else:
            syn_logger.warning('EEW Log FileDir Not Found!')
        if 'jeew_dir' in vars(self):
            jeew_list = os.listdir(self.jeew_dir)
            self.jeew_list = jeew_list
            self.jeew_outdir = os.path.join(self.batch_outdir, 'JEEW')
            os.mkdir(self.jeew_outdir)
        else:
            syn_logger.warning('JEEW Log FileDir Not Found!')

        for i in range(0, self.evt_num):
            evt_para_1 = self.bianmu_result.iloc[i, :].values.tolist()
            evt_para_2 = [str(evt_para) for evt_para in evt_para_1[:-2]]
            evt_para_3 = [evt_para_2[0], evt_para_2[3], evt_para_2[2], evt_para_2[1],
                          0]  # last 0 for default timeshift
            ## Process For eew logfile
            if 'eew_outdir' in vars(self):
                eew_log_dir = self.find_eewlog(evt_para_1[0])
                if eew_log_dir:
                    el = eewlog_read(eew_log_dir, evt_para_3, self.eew_outdir)
                    el.main()
            ## Process For jeew logfile
            if 'jeew_outdir' in vars(self):
                jeew_log_file = self.find_jeewlog(evt_para_1[0])
                print(jeew_log_file, evt_para_1[0])
                if jeew_log_file:
                    jl = jeewlog_read(jeew_log_file, evt_para_3, self.jeew_outdir)
                    jl.line_parse()

    def find_eewlog(self, evt_time):
        time_diff = [abs(float(evt_time) - float(eew_time_list)) for eew_time_list in self.eew_list]
        # if self.evt_num == len(self.eew_list):
        #     print('日志文件与编目结果：一一对应')
        # else:
        #     print(f'eew日志文件缺失{self.evt_num - len(self.eew_list)}')
        if min(time_diff) < 5:  # 发震时刻与日志文件相差±5秒认为是对应
            evt_dir = self.eew_list[time_diff.index(min(time_diff))]
            syn_logger.info(f'{datetime.datetime.now()}：EEW——EVT_TIME:{evt_time} correspond LogFileDir:{evt_dir}')
            return os.path.join(self.eew_dir, evt_dir)
        else:
            syn_logger.info(f'{datetime.datetime.now()}：EEW——EVT_TIME:{evt_time} correspond LogFileDir Not Found!')
            return None

    def find_jeewlog(self, evt_time):
        log_select = None
        evt_date = datetime.datetime.strptime(str(evt_time), '%Y%m%d%H%M%S.%f')
        evt_date1 = evt_date.strftime('%Y%m%d')
        for file in self.jeew_list:
            if evt_date1 in file:
                syn_logger.info(f'{datetime.datetime.now()}：JEEW——EVT_TIME:{evt_time} correspond LogFileDir:{file}')
                log_select = os.path.join(self.jeew_dir, file)
                return log_select
        if log_select is None:
            syn_logger.warning(f'{datetime.datetime.now()}：JEEW——EVT_TIME:{evt_time} correspond LogFileDir Not Found!')


class eewlog_read(pqc.QObject):
    first_sta_signal = pqc.pyqtSignal(str)

    def __init__(self, filedir, evt_para, out_dir=r'./out'):
        super(eewlog_read, self).__init__()
        self.filepath = filedir
        self.outdir = out_dir
        self.pub_index = []
        self.station_list = []
        self.assit = assit()
        self.evt = evt_para  # time,level,lon,la,time_shift
        self.outdata = pd.DataFrame(
            columns=['处理序号', '发布序号', '发布时间', '预警日志用时', '实际用时', '使用台站数', '预警发震时间',
                     '纬度', '经度', '预警震级', '预警深度', '震中烈度',
                     '编目发震时刻',
                     '编目纬度', '编目经度', '编目震级', '震级偏差', '定位偏差（km）', '发震时刻偏差'],
            dtype=pd.StringDtype())
        self.sta_detail = pd.DataFrame(columns=['台站名', '触发时间', '纬度', '精度', '震中距', 'Mpd', 'Mri', '持时'],
                                       dtype=pd.StringDtype())
        # self.main()

    def read_dir(self):
        files = os.listdir(self.filepath)
        for log_file in files:
            if 'process' in log_file:
                self.ej = open(os.path.join(self.filepath, log_file), 'r', encoding='utf-8')
                self.log_text = self.ej.read().strip().split('*' * 71)
                self.log_text = self.log_text[:-1]
            if 'promul' in log_file:
                self.pub_index.append(log_file.split('.')[1])
        self.pub_index = [int(x) for x in self.pub_index]

    def para_parse(self, para):
        syn_logger.info(f'{datetime.datetime.now()}:Earthquake Parameter Extraction Start')
        content = para.strip().split('\n')
        eew_process_time = content[0].split(' ')[0].replace('当前时间：', '').replace(',', '').replace(':', '')
        eew_process_index = content[0].split(' ')[2].replace('处理序号：', '')
        syn_logger.info(f'{datetime.datetime.now()}:EEW LogFile {eew_process_index}th Result')
        use_sta_num = content[1].split(':')[1].split('-')[0].strip('P')
        loc_sta_num = content[1].split(':')[1].split('-')[1].strip('M')
        syn_logger.info(
            f'{datetime.datetime.now()}：total used station:{use_sta_num}, num for station to loc:{loc_sta_num}')
        eew_pub_index = content[0].split(' ')[3].replace('发布序号：', '')
        eew_eq_time = content[2].replace('时间:', '').replace(',', '').replace(':', '')
        cor = content[3].strip('震中:').split('   ')
        lon, la, depth = cor[0].strip('(E)'), cor[1].strip('(N)'), cor[2].strip('(Z)')
        level = content[4].strip('震级:').split(' ')[0].strip('(M)')
        process_time = content[6].split('（s）:')[2].split('\t')[0]  # 预警用时
        try:
            intensity = content[5].split(':')[1]
        except:
            intensity = ''
        syn_logger.info(f'{datetime.datetime.now()}:EEW Station Analysis Start')
        for line_index in range(9, len(content)):
            if 'P波触发' in content[line_index]:
                station_name = content[line_index].split('\t')[0].split('.')[1]
                trigger_time = content[line_index].split('\t')[1].split('：')[1].replace(',', '').replace(':', '')
                station_lon = content[line_index].split('\t')[2]
                station_la = content[line_index].split('\t')[3]
                station_epicdis = content[line_index].split('\t')[4]
                mpd = content[line_index].split('\t')[6].split('/')[0]
                last_time = content[line_index].split('\t')[6].split('/')[1]  # 持续时间
                mri = content[line_index].split('\t')[7].split('/')[0]
                self.station_list.append(station_name)
                if int(eew_process_index) == self.pub_index[0]:  # 第一报时输出台站对应参数，根据处理序号选择
                    detail = [station_name, trigger_time, station_la, station_lon, station_epicdis, mpd, mri, last_time]
                    self.sta_detail.loc[len(self.sta_detail)] = detail
            else:
                continue
        syn_logger.info(f'after all station:{self.station_list}')

        eew_ptime = datetime.datetime.strptime(eew_process_time, '%Y%m%d%H%M%S.%f')

        bianmu_eqtime = datetime.datetime.strptime(self.evt_new, '%Y%m%d%H%M%S.%f')
        real_time_diff = str((eew_ptime - bianmu_eqtime).seconds) + '.' + str((eew_ptime - bianmu_eqtime).microseconds)
        real_time_diff = '{:.2f}'.format(float(real_time_diff))
        eqtime_list = [float(eew_eq_time), float(self.evt_new)]
        eq_timddiff_mid = datetime.datetime.strptime(str(max(eqtime_list)),
                                                     '%Y%m%d%H%M%S.%f') - datetime.datetime.strptime(
            str(min(eqtime_list)), '%Y%m%d%H%M%S.%f')
        eq_time_diff = str(eq_timddiff_mid.seconds) + '.' + '%06d' % eq_timddiff_mid.microseconds
        eq_time_diff = '{:.2f}'.format(float(eq_time_diff.rstrip('0')))
        alarm_res = [eew_process_index, eew_pub_index, eew_process_time, process_time, real_time_diff,
                     len(self.station_list),
                     eew_eq_time, la, lon, level, depth, intensity, self.evt_new, self.evt[3], self.evt[2], self.evt[1],
                     '{:.2f}'.format(float(level) - float(self.evt[1])),
                     '{:.2f}'.format(
                         self.assit.calc_distance([float(lon), float(la)], [float(self.evt[2]), float(self.evt[3])])),
                     eq_time_diff]
        self.outdata.loc[len(self.outdata.index)] = alarm_res
        self.station_list = []

    def main(self):
        self.read_dir()
        self.evt_new = self.assit.time_shift(self.evt[0], self.evt[-1])

        for logtxt in self.log_text:
            self.para_parse(logtxt)
        self.outpath = self.outdir + '/' + '{:.2f}'.format(float(self.evt_new))
        os.mkdir(self.outpath)
        self.outdata.loc[:, '发布序号'] = ''
        for i in range(0, len(self.pub_index)):
            self.outdata.loc[self.pub_index[i] - 1, '发布序号'] = i + 1
        syn_logger.info(f'{datetime.datetime.now()}:EEW Analysis Result Output')
        self.outdata.to_excel(self.outpath + '/result.xlsx', index=False, header=True)
        syn_logger.info(f'{datetime.datetime.now()}:EEW First Report Station Analysis Result Output')
        self.sta_detail.to_excel(self.outpath + '/First_report.xlsx', index=False, header=True)
        self.first_sta_signal.emit(f'{self.evt[0]}-EEW首报台站:'+'|'.join(self.sta_detail['台站名'].tolist()))  # for 界面显示首报 发送信号


class jeewlog_read(pqc.QObject):
    first_sta_signal = pqc.pyqtSignal(str)

    def __init__(self, filedir, evt_para, out_dir=r'./out'):
        super( jeewlog_read, self).__init__()
        self.filepath = filedir
        self.outdir = out_dir
        self.evt = evt_para
        self.outdata = pd.DataFrame(
            columns=['发布时间', '预警日志用时', '实际用时', '使用台站数', '预警发震时间', '纬度', '经度', '预警震级',
                     '预警深度',
                     '编目发震时刻', '编目纬度', '编目经度', '编目震级', '震级偏差', '定位偏差（km）', '发震时刻偏差'],
            dtype=pd.StringDtype())
        self.sta_detail = pd.DataFrame(columns=['台站名', '触发时间', '震中距', 'ML', 'Mri', '持时'],
                                       dtype=pd.StringDtype())
        self.station_list = []
        self.assit = assit()
        # self.line_parse()

    def line_parse(self):
        self.evt_new = self.assit.time_shift(self.evt[0], self.evt[-1])

        if os.path.isdir(self.filepath):
            # filepath is dir
            file_list = os.listdir(self.filepath)
            for jeew_i in file_list:
                if '.' in jeew_i and 'report' in jeew_i:
                    jeew_file = jeew_i
                    self.fj = open(os.path.join(self.filepath, jeew_file), 'r')
        elif os.path.isfile(self.filepath):
            # filepath is file
            jeew_file = self.filepath
            self.fj = open(jeew_file, 'r')

        self.log_text = self.fj.read().strip().split('\n')
        evt_start = datetime.datetime.strptime(self.evt_new, '%Y%m%d%H%M%S.%f').strftime('%Y%m%d%H%M')
        header_index_list = []
        self.evt_start = evt_start
        for i in range(len(self.log_text)):
            if self.log_text[i].startswith(evt_start):
                res = list(filter(None, self.log_text[i].split(' ')))
                publish_judge = res[-6]
                if publish_judge == '1':
                    header_index_list.append(i)
            else:
                continue
        ## if evt_start is more than real_time_flag in report_*.txt
        if header_index_list == []:
            self.evt_start = str(int(self.evt_start) - 1)
            for i in range(len(self.log_text)):
                if self.log_text[i].startswith(self.evt_start):
                    res = list(filter(None, self.log_text[i].split(' ')))
                    publish_judge = res[-6]
                    if publish_judge == '1':
                        header_index_list.append(i)
                else:
                    continue

        for hi in range(len(header_index_list)):  # hi stands for index to header_index_list
            index = header_index_list[hi]
            if hi != len(header_index_list) - 1:
                index_next = header_index_list[hi + 1]
            else:
                index_next = len(self.log_text) - 1
            # 处理事件头部数据
            syn_logger.info(f'{datetime.datetime.now()}:Earthquake Event Header Process Start')
            res = list(filter(None, self.log_text[index].split(' ')))
            self.header_parse(res)
            # 跳过头部数据行，进入台站数据处理
            # index = index + 1
            # while index <= index_next:
            #     if self.log_text[index] != '    ':
            #         self.station_parse(self.log_text[index])
            #         index += 1
            #     else:
            #         break

            i = index + 1

            while i < header_index_list[1] if len(header_index_list) != 1 else index_next:
                print(f'{datetime.datetime.now()}:Event First Report Station Process Start-{i}th Station')
                if self.log_text[i] != '    ':
                    self.station_parse(self.log_text[i])
                    i += 1
                else:
                    break
        self.outpath = self.outdir + '/' + '{:.2f}'.format(float(self.evt_new))
        os.mkdir(self.outpath)
        syn_logger.info(f'{datetime.datetime.now()}:JEEW Analysis Result Output')
        self.outdata.to_excel(self.outpath + '/result.xlsx', index=True, header=True)
        syn_logger.info(f'{datetime.datetime.now()}:JEEW First Report Station Analysis Result Output')
        self.sta_detail.to_excel(self.outpath + '/First_report.xlsx', index=False, header=True)
        self.first_sta_signal.emit(f'{self.evt[0]}-JEEW首报台站:'+'|'.join(self.sta_detail['台站名'].tolist()))  # for 界面显示首报 发送信号

    def header_parse(self, result):
        res = result
        pub_time = datetime.datetime.strptime(res[0][:-2], '%Y%m%d%H') + datetime.timedelta(minutes=int(res[-2]),
                                                                                            seconds=float(res[-1]))
        eq_time = datetime.datetime.strptime(res[0], '%Y%m%d%H%M') + datetime.timedelta(seconds=float(res[2]))
        la = res[4]
        lon = res[6]
        depth = res[8]
        level = res[10]
        station_num = res[-7]
        process_time = res[-5]
        bianmu_eqtime = datetime.datetime.strptime(self.evt_new, '%Y%m%d%H%M%S.%f')
        real_process_time = str((pub_time - bianmu_eqtime).seconds) + '.' + str((pub_time - bianmu_eqtime).microseconds)
        real_process_time = '{:.2f}'.format(float(real_process_time))
        eqtime_list = [float(eq_time.strftime('%Y%m%d%H%M%S.%f')), float(self.evt_new)]
        eq_timddiff_mid = datetime.datetime.strptime(str(max(eqtime_list)),
                                                     '%Y%m%d%H%M%S.%f') - datetime.datetime.strptime(
            str(min(eqtime_list)), '%Y%m%d%H%M%S.%f')
        eq_time_diff = str(eq_timddiff_mid.seconds) + '.' + '%06d' % eq_timddiff_mid.microseconds
        eq_time_diff = '{:.2f}'.format(float(eq_time_diff.rstrip('0')))
        pub_time = '{:.2f}'.format(float(pub_time.strftime('%Y%m%d%H%M%S.%f')))
        eq_time = '{:.2f}'.format(float(eq_time.strftime('%Y%m%d%H%M%S.%f')))

        self.header_lis = [pub_time, process_time, real_process_time, station_num, eq_time, la, lon, level, depth,
                           self.evt_new, self.evt[3], self.evt[2], self.evt[1],
                           '{:.2f}'.format(float(level) - float(self.evt[1])), '{:.2f}'.format(
                self.assit.calc_distance([float(lon), float(la)], [float(self.evt[2]), float(self.evt[3])])),
                           eq_time_diff]

        self.outdata.loc[len(self.outdata.index)] = self.header_lis

    def station_parse(self, station_line):
        res = list(filter(None, station_line.split(' ')))
        if res[3] != '-9.90':
            station_name = res[0].replace('SC/', '')
            trigger_time = datetime.datetime.strptime(self.evt_start, '%Y%m%d%H%M') + datetime.timedelta(
                seconds=float(res[2]))
            trigger_time = '{:.2f}'.format(float(trigger_time.strftime('%Y%m%d%H%M%S.%f')))
            eq_epicdis = res[-8]
            ml = res[-10]
            mri = res[-5]
            last_time = str(int(res[-1]))
            detail = [station_name, trigger_time, eq_epicdis, ml, mri, last_time]
            self.sta_detail.loc[len(self.sta_detail.index)] = detail


class assit:

    def calc_distance(self, a, b):
        # '距离计算' index:1 la,0 lon
        distance = 6371004 * math.acos((math.sin(math.radians(a[1])) * math.sin(math.radians(b[1])) + math.cos(
            math.radians(a[1])) * math.cos(math.radians(b[1])) * math.cos(math.radians(b[0] - a[0]))))
        return distance / 1000

    def time_shift(self, evt_time, shift_time):
        real_eq_time = datetime.datetime.strptime(str(evt_time), '%Y%m%d%H%M%S.%f')
        shiyi = int(shift_time)  # 时移
        evt_after = real_eq_time + datetime.timedelta(minutes=shiyi)
        return evt_after.strftime('%Y%m%d%H%M%S.%f')


class new_batch(pqc.QObject):
    nb_signal = pqc.pyqtSignal(str)

    def __init__(self, filepath):
        super(new_batch, self).__init__()
        self.fpath = filepath
        self.outdata = pd.DataFrame()
        dir_list = os.listdir(self.fpath)
        self.contents = os.listdir(os.path.join(self.fpath, dir_list[random.randint(0, len(dir_list) - 1)]))
        try:
            self.outpath = os.path.join(os.path.split(filepath)[0], 'output')
            os.mkdir(self.outpath)
        except FileExistsError:
            shutil.rmtree(self.outpath)
            os.mkdir(self.outpath)
            syn_logger.info('out文件夹已存在')

    def type_judge(self):
        if 'txt' in self.contents[0]:
            self.jeew_batch()
        else:
            self.eew_batch()

    def jeew_batch(self):
        file_list1 = os.listdir(self.fpath)
        for file_j in file_list1:  # file_j is event_dir_name
            print(file_j)
            self.nb_signal.emit(f'当前处理地震事件：{file_j}')
            out_dir = os.path.join(self.outpath, file_j)
            try:
                os.mkdir(out_dir)
            except FileExistsError:
                syn_logger.info(f'{file_j}: Event Dir exists')
            # print(file_j)
            # from event_dir construct evt_para,last 0 stands for default timeshift
            evt_pata = [file_j.split('_')[0].split('.')[1] + '.0', file_j.split('_')[1].strip('M'),
                        file_j.split('_')[3],
                        file_j.split('_')[2], 0]
            # print(evt_pata)
            # os.remove(os.path.join(filepath, file_j, 'gen_net.ch'))
            next_dir = os.path.join(self.fpath, file_j)  # next_dir is log_dir in event_dir_name
            if os.listdir(next_dir):
                jl = jeewlog_read(next_dir, evt_pata, out_dir)
                jl.line_parse()
                jl.outdata['事件名'] = file_j
                self.outdata = pd.concat([self.outdata, jl.outdata])
            else:
                syn_logger.warning(f"\n{next_dir}: No reports.txt in event_dir\n")
        self.outdata.to_excel(os.path.join(self.outpath, 'final_res.xlsx'))

    def eew_batch(self):
        file_list1 = os.listdir(self.fpath)
        for file_j in file_list1:  # file_j is event_dir_name
            print(file_j)
            self.nb_signal.emit(f'当前处理地震事件：{file_j}')
            # from event_dir construct evt_para,last 0 stands for default timeshift
            evt_pata = [file_j.split('_')[0].split('.')[1] + '.0', file_j.split('_')[1].strip('M'),
                        file_j.split('_')[3],
                        file_j.split('_')[2], 0]
            print(evt_pata)
            # os.remove(os.path.join(filepath, file_j, 'gen_net.ch'))
            next_dir = os.listdir(os.path.join(self.fpath, file_j))  # next_dir is log_dir in event_dir_name
            for log_file in next_dir:  # log_file is log_dir_name
                if '_bak' not in log_file:
                    out_dir = os.path.join(self.outpath, file_j)
                    try:
                        os.mkdir(out_dir)
                    except FileExistsError:
                        syn_logger.info('Event Dir exists')
                    res_dir = os.path.join(out_dir, log_file)
                    os.mkdir(res_dir)
                    log_list = os.listdir(os.path.join(self.fpath, file_j, log_file))
                    if len(log_list) != 1:
                        el = eewlog_read(os.path.join(self.fpath, file_j, log_file), evt_pata,
                                         res_dir)  # eew logfile analysis
                        el.main()
                        el.outdata['事件名'] = file_j
                        self.outdata = pd.concat([self.outdata, el.outdata])
                else:
                    continue
        self.outdata.to_excel(os.path.join(self.outpath, 'final_res.xlsx'))


class batch_thread(pqc.QThread):
    bt_msg_signal = pqc.pyqtSignal(str)
    end_signal = pqc.pyqtSignal()

    def __init__(self, filepath):
        super(batch_thread, self).__init__()
        self.fpath = filepath

    def run(self):
        nb = new_batch(self.fpath)
        nb.nb_signal.connect(self.signal_handler)
        nb.type_judge()
        self.end_signal.emit()

    def signal_handler(self, msg_str):
        self.bt_msg_signal.emit(msg_str)


if __name__ == '__main__':
    bm = bianmu_read('./logfile_test/')
    # bm = bianmu_read('./logfile/')
